# -*- coding: utf-8 -*-
import base64
import binascii
import json
from collections import OrderedDict

import requests
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Hash import SHA
from Crypto.PublicKey import RSA
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
from django.conf import settings

from async import async_job
from common.cache import redis_cache
from common.channel.pay import check_channel_order
from common.order import db as order_db
from common.order.model import PAY_STATUS
from common.utils import track_logging
from common.utils.exceptions import ParamError

_LOGGER = track_logging.getLogger(__name__)

APP_CONF = {
    '3765': {
        # _onepaycashpay支付 手机收银台 50-5000 2.8% DWC
        'gateway': 'https://api.onepay.solutions/payment/otoSoft/v3/mweb.html',
        'query_gateway': 'https://api.onepay.solutions/payment/otoSoft/v3/queryOrder.html',
        'pub_key': """-----BEGIN PUBLIC KEY-----
    MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDmwwFxxkClvsBzZUtE0CN4S7P0QWZxnpxn2De0zlqbjY6Put/8738SXYkGsuBIb5QZU3tDb/0hmON3zQ84BLexksP2iNqY1q1VSeY2NkV/QxrCUefUedTFsDU+ZcIB5JJ02m4fqpYtzYowtf5JrgjYHcyrO1IaX3NVITm9EPOMHQIDAQAB
    -----END PUBLIC KEY-----""",
        'pri_key': """-----BEGIN PRIVATE KEY-----
    MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAJMtJBuKcQq6l6dJ1z31VghRJF4SAy2b3YEulOlmrE49yWxzk8S4tngP1jIU6N7h67bI6oaDjKWSjv44JWgFlp/0pV0Na1GY+dLtimMkz9Rqh8dPJgGLvFQNeeiNpObCk6u9rSkbPMbfyaBqotVfb8LurkFpLK7sqUYZmpBmBvo5AgMBAAECgYBcurhhUdzfcalTkca4FvfZzaYoTxkJh4cMeZkBZdtygKOEXV3hOLBe/Ttr2pjOtXGafQSnNANN3wWRTzbiT4MS2IE5NbEpKn9RSyoKUVe7keg9IEWMVNe+W5xefQhOprl88hY0hiR/set7sgQEbaba6oZ/HEfoHcEcuBgOekuccQJBAMly+5B0rWu9puTIGulyiokDtYydt+HqCtw2punVstwt26+JXGTNcGefooE9fb4yLYicemeuKGTB6VjwRQutIw8CQQC7B9CrMujySOuEQxQ1GjOgRVvU0UR3LnGkwm/7ApaUyIof7Q1WLwXIhJKShtr60aekZ8UnS0N2z0Rke4hE/G43AkEAlzkOqYE9Eg0bdNDWivpmDrbPGZDoEdosbuVD5XN7QhfWu24lArLJt7A5QEWV2Co4zj2REbXGMuTyM8aiPkKpdwJAKbeSib0vTGuLbNLxfO69OTB/TTboSJUpcBCWnax0HfelJ33ejayrY5B+iQRfb7a35+nlheA5yhRtMuSC/rA3fQJABFsLEwfZpO0m9MKukOJDCEppncOFSJutKAgRRoihEj/TNJ2dgUX6UHIDypo9zyNO3Cc/8u/huYEoalFhfYBDhg==
    -----END PRIVATE KEY-----""",
    },
    '3885': {
        # _onepaycashpay支付 手机收银台 50-5000 2.8% dbl
        'gateway': 'https://api.onepay.solutions/payment/otoSoft/v3/mweb.html',
        'query_gateway': 'https://api.onepay.solutions/payment/otoSoft/v3/queryOrder.html',
        'pub_key': """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDmwwFxxkClvsBzZUtE0CN4S7P0QWZxnpxn2De0zlqbjY6Put/8738SXYkGsuBIb5QZU3tDb/0hmON3zQ84BLexksP2iNqY1q1VSeY2NkV/QxrCUefUedTFsDU+ZcIB5JJ02m4fqpYtzYowtf5JrgjYHcyrO1IaX3NVITm9EPOMHQIDAQAB
-----END PUBLIC KEY-----""",
        'pri_key': """-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAK1zHTJ9234MadfLkRsJpVlQDWurvyYc9A05XPvau2E/Bi+fEn3uwA8dXv7BJSxYZgrPgXUlywow7G0tCmgTj3qmc1GAtQ13LQVi3Th3OBL0vgv0tLqNrA7BIIcGVZfDOLJbXB6MZinBmGHOhwaw0VScLpYnmT26sfIbvnJYqujTAgMBAAECgYAAzqVXI9DOsF4Zu//L4WqclMvLMXxtP/s+yIKPRYBTvohX1mSuo1rPdzKG+v0iTLME39xZYDimrn2bMHd47oZfaMbzq9wL/qKR9FJSBg+jo5o7a/xm2I8oaJib36ygu5m+KaMnulMB4p5fLKVWQhRgjkE5aNrVr5Ag0ifVlTdxKQJBAO5y7a9uJ3w2C1bvlrHUqLSsdc707HKW1kR6cb3BRH6vBYesxhNYnohosh2islHTkruSibasL5pBY8jUtAL18/8CQQC6N2kDCOjD7u2MHgYsEkAUh+nmG29K7B6segr5QpEv/orvHdUh+73l4B9PGUnzBZ4YMOTs8aMyONZE7v/CzPstAkAiUxT3/eldLgJv30lYC/7FE1ZaFlO7Iw9xCBc7c4jCm7s2Dp7sxgL8K+YH8hWtRcGHks6UJzErCWKt1ECddH5NAkEAmAJHbLzHFbpim3Ce5Tb4rEnOe5KFpQleeLkfYB4g2qbUzyDxLM6NU6tCo5UnMoSxa1nuZiVSbNrMvnTCv0gI+QJARA/tve+MLm+Y9UnB7S8PY2mlJsEHSuzGTxXWjUoFwNPPzV5dd4ttssOVaadiJ9NVSJD7eL5YqxItehZUueJBhg==
-----END PRIVATE KEY-----""",
    },
    '3931': {
        # _onepaycashpay支付 手机收银台 50-5000 2.8% tt
        'gateway': 'https://api.onepay.solutions/payment/otoSoft/v3/mweb.html',
        'query_gateway': 'https://api.onepay.solutions/payment/otoSoft/v3/queryOrder.html',
        'pub_key': """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDmwwFxxkClvsBzZUtE0CN4S7P0QWZxnpxn2De0zlqbjY6Put/8738SXYkGsuBIb5QZU3tDb/0hmON3zQ84BLexksP2iNqY1q1VSeY2NkV/QxrCUefUedTFsDU+ZcIB5JJ02m4fqpYtzYowtf5JrgjYHcyrO1IaX3NVITm9EPOMHQIDAQAB
-----END PUBLIC KEY-----""",
        'pri_key': """-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJ8Nyu5zVADnp4RL4Pj9v1BjxoBkOtCXQodu5Vj0eFeL6L2IyW7S+2UGlCIXxaVv/+EMiD0IPVzKiFuPWVlHvOkDuS7ISrLAG/EkLtEzxXWun07MrYOgOQ5Hc7kb3HC5IuINiaLfoqDlLHsIP1TMbRHpLbjkTFdmJ5m2ITqEFEbfAgMBAAECgYBnDCyZ6KZYH73sfKy5JM06bCpDLKzeT+GOlU6KH3mIXuDfSywWXSL7BRQcMoKe+L0zNUdfI2N+JsnJaEpfCZdd6fY4RfxxYDB4qPMLOVw1sq8K09ZhmQ02KOJ9rK6lXyg4nhwVdvcFaGZuy5jvuZpc0p/GfBdzbHrGiTP/8jMagQJBANq0Tfyf75u3JAXWQlpBXaltxZ+4KS4Nw/xo+/siLM7OGmU+/v4wxjzNfK5v3RnTsWON5MBMaQjB59lYCJuSFC8CQQC6LWet/8D3xDny/FWR3kvgy/iHmzIsn5YUY6qJ7FfyECUSZr0i/ccUDbmIhrw/F1MVPIJJ+Nf8HvElJStoVVxRAkEAvP2cetbWnvAm7+hUFFxyDKxwX3IsG8EHgIJUZZMFt3xBMQa8IXqShA5qVO8T2HKn0sjWSRp+lXPC332EirM0PwJASt6FijiGblzv/OIyuyVNGEqOWGI17DIFFY/6/doYh9SsQ9kjCFZL36mhSeD9BNYpaCncL0kt+kqrKQfoi91/8QJBAJl7tdEEpi9wY+9Pi6Xe6L9ZLh3SZ1xNFGxtTpQEn8yzlKQc0r8xFYQeSQWFCneORV26tc1OQRPXtW3L2ETXzUE=
-----END PRIVATE KEY-----""",
    },
    '3933': {
        # _onepaycashpay支付 手机收银台 50-5000 2.8% loki
        'gateway': 'https://api.onepay.solutions/payment/otoSoft/v3/mweb.html',
        'query_gateway': 'https://api.onepay.solutions/payment/otoSoft/v3/queryOrder.html',
        'pub_key': """-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDmwwFxxkClvsBzZUtE0CN4S7P0QWZxnpxn2De0zlqbjY6Put/8738SXYkGsuBIb5QZU3tDb/0hmON3zQ84BLexksP2iNqY1q1VSeY2NkV/QxrCUefUedTFsDU+ZcIB5JJ02m4fqpYtzYowtf5JrgjYHcyrO1IaX3NVITm9EPOMHQIDAQAB
-----END PUBLIC KEY-----""",
        'pri_key': """-----BEGIN PRIVATE KEY-----
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANVQEJxdHTNEzpMHRS7ZInxUnWGq6KvZjnWMJYnDzTYRg2QuKILakgXOhA5ueAfhyq+6HsGS63GPh7EUyRCvUY8g8mMK4MzlVcq1CTtjKHOYvFVahipRWgyVeNdhaTfZe9bmhPvLZpJkAQuUQ4KAupxTCvSi6NCtHTrJERxU/zslAgMBAAECgYBC5MDQyEKH+WPup7ECaAVwh/hy0G32tlr+cdyzvztzYTqbB/6cSn7QY1r2S55Imn49bViHy9MZ4bDjz2jeWpxwezH0Qwf5FIGm0xhHtvPfgjt8EH5vFBDVjrHnrgZv1pN1Hr41sT1yCRPZkSAVMKo2fqLDM7oIvbgxyuYd8owQAQJBAPpqGXDbt7xvRTblXckxg3lt4IT9aPKfJS/zn6g72Pom9JW+kxYbThLmgkI8Wymcz+0v0L8P70pQvOPxJXmHeKUCQQDaEhwZa0WiI905l5+g/rrVVEHdE5Kaa9yKZb+leFVxYH88gTJn7UGlCPcSh9KHdDSFbea5VMn4qQXHVKsdgrCBAkEA7RNR7rU1qGK35pcUSYxk6quJ6p53o2vkKxe9SesPKxWCbdq1KjLDocU2ATtfG3BosieYu6p8Y6E4k50UW5BUOQJACnOVvy4h/zxizPDUaL3srG7GXVcjzpzezA9GWSLkTXPHhVnX1Z1MaSF93fh+gZlzLvXuefFzYKSRL1WCgf6SgQJBAJvQnxL/YcLQyXwnoR7dBlQ8qb8bDkQjCRLBvGi6VgnPiTcz1v6NEhW34LxapZn52uaw/ld/mT2hUBAP3cou3CU=
-----END PRIVATE KEY-----""",
    },
}


def _get_gateway(mch_id):
    return APP_CONF[mch_id]['gateway']


def _get_query_gateway(mch_id):
    return APP_CONF[mch_id]['query_gateway']


def _get_pri_key(mch_id):
    return APP_CONF[mch_id]['pri_key']


def _get_pub_key(mch_id):
    return APP_CONF[mch_id]['pub_key']


def dec_to_hex(dec_num):
    return hex(int(dec_num, 10))


def str2hex(string):
    hex_str = ''
    for char in string:
        int_char = ord(char)
        hex_num = hex(int_char).lstrip("0x")
        hex_str += hex_num
    return hex_str


def hex2str(string):
    clear_str = ''
    for counter in xrange(0, len(string), 2):
        hex_char = string[counter] + string[counter + 1]
        clear_str += binascii.unhexlify(hex_char)
    return clear_str


def _gen_sign(message, app_id):
    key = RSA.importKey(_get_pri_key(app_id))
    h = SHA.new(message)
    signer = Signature_pkcs1_v1_5.new(key)
    signature = signer.sign(h)
    return base64.b64encode(signature)


def _gen_pub_sign(message, app_id):
    key = RSA.importKey(_get_pub_key(app_id))
    h = SHA.new(message)
    signer = Signature_pkcs1_v1_5.new(key)
    signature = signer.sign(h)
    return base64.b64encode(signature)


def verify(message, signature, key):
    # _LOGGER.info("_onepaycashpay verify app_id is: %s", key)
    pub_key = RSA.importKey(key)
    h = SHA.new(message)
    verify_result = Signature_pkcs1_v1_5.new(pub_key).verify(h, signature)
    # _LOGGER.info("_onepaycashpay verify_result is: %s, 0 mean success", verify_result)
    return verify_result


def generate_sign(parameter):
    s = ''
    for k in sorted(parameter.keys()):
        if k != 'sign' and k != 'signType' and parameter[k]:
            s += '%s=%s&' % (k, parameter[k])
    # _LOGGER.info("_onepaycashpay sign str is: %s", s[:len(s) - 1])
    return s[:len(s) - 1]


def generate_create_res_sign(parameter):
    s = ''
    for k in sorted(parameter.keys()):
        if parameter[k]:
            s += '%s=%s&' % (k, parameter[k])
    # _LOGGER.info("_onepaycashpay create response sign str is: %s", s[:len(s) - 1])
    return s[:len(s) - 1]


def verify_create_response_sign(params, app_id):
    sign = params['sign']
    params.pop('sign')
    params_to_sign = generate_create_res_sign(params)
    if verify(params_to_sign, hex2str(sign), _get_pub_key(app_id)) != 0:
        raise ParamError('_onepaycashpay sign not pass, data: %s' % params)


def verify_notify_sign(params, app_id):
    sign = params['sign']
    params.pop('sign')
    params_to_sign = generate_sign(params)
    if verify(params_to_sign, sign, _get_pub_key(app_id)) != 0:
        raise ParamError('_onepaycashpay sign not pass, data: %s' % params)


def _get_pay_type(service):
    if service == 'alipay':
        return 'ALIPAY'
    return 'ALIPAY'


def rsa_long_encrypt(pub_key_str, msg, length=117):
    """
    单次加密串的长度最大为 (key_size/8)-11
    1024bit的证书用100， 2048bit的证书用 200
    """
    pubobj = RSA.importKey(pub_key_str)
    pubobj = Cipher_pkcs1_v1_5.new(pubobj)
    res = []
    for i in range(0, len(msg), length):
        res.append(pubobj.encrypt(msg[i:i + length]))
    return "".join(res)


def rsa_long_decrypt(priv_key_str, msg, length=128):
    """
    1024bit的证书用128，2048bit证书用256位
    """
    privobj = RSA.importKey(priv_key_str)
    privobj = Cipher_pkcs1_v1_5.new(privobj)
    res = []
    for i in range(0, len(msg), length):
        res.append(privobj.decrypt(msg[i:i + length], ''))
    return "".join(res)


def _build_form(params, gateway):
    html = u"<head><title>loading...</title></head><form id='submit' name='submit' action='" + gateway + "' method='post'>"
    for k, v in params.items():
        html += "<input type='hidden' name='%s' value='%s'/>" % (k, v)
    html += "</form>"
    html += "<script>doc" \
            "ument.forms['submit'].submit();</script>"
    return html


def create_charge(pay, pay_amount, info):
    service = info.get('service')
    app_id = info['app_id']
    parameter_dict = OrderedDict((
        ('app_id', app_id),
        ('order_no', str(pay.id)),
        ('body', 'OnePayPhoneCash'),
        ('currency', 'CNY'),
        ('amount', '%.2f' % pay_amount),
        ('payment_channel', _get_pay_type(service)),
        ('signType', 'RSA'),
    ))
    sign_str = generate_sign(parameter_dict)
    parameter_dict['sign'] = str2hex(_gen_sign(sign_str, app_id))
    _LOGGER.info("_onepaycashpay create_charge data: %s, order_id: %s", json.dumps(parameter_dict),
                 parameter_dict['order_no'])
    html_text = _build_form(parameter_dict, _get_gateway(app_id))
    cache_id = redis_cache.save_html(pay.id, html_text)
    return {'charge_info': settings.PAY_CACHE_URL + cache_id}


# SUCCESS
def check_notify_sign(request, app_id):
    data = dict(request.POST.iteritems())
    _LOGGER.info("_onepaycashpay notify request data: %s, order_id: %s", data, data['order_no'])
    verify_notify_sign(data, app_id)
    pay_id = data['order_no']
    if not pay_id:
        _LOGGER.error("fatal error, out_trade_no not exists, data: %s", data)
        raise ParamError('_onepaycashpay event does not contain pay ID')

    pay = order_db.get_pay(str(pay_id))
    if not pay:
        raise ParamError('pay_id: %s invalid' % pay_id)
    if pay.status != PAY_STATUS.READY:
        raise ParamError('pay %s has been processed' % pay_id)

    mch_id = pay.mch_id
    trade_status = data['status']
    total_fee = float(data['amount'])
    trade_no = data['payment_id']
    extend = {
        'trade_status': trade_status,
        'trade_no': trade_no,
        'total_fee': total_fee
    }
    check_channel_order(pay_id, total_fee, app_id)
    # 支付成功:PS_PAYMENT_SUCCESS 支付失败:PS_PAYMENT_FAIL
    if trade_status == 'PS_PAYMENT_SUCCESS' and total_fee > 0.0:
        _LOGGER.info('_onepaycashpay check order success, mch_id:%s pay_id:%s' % (mch_id, pay_id))
        order_db.add_pay_success(mch_id, pay_id, total_fee, trade_no, extend)
        # async notify
        async_job.notify_mch(pay_id)


def query_charge(pay_order, app_id):
    ''' 查询订单 '''
    pay_id = pay_order.id
    parameter_dict = OrderedDict((
        ('app_id', app_id),
        ('order_no', str(pay_id)),
        ('signType', 'RSA'),
    ))
    sign_str = generate_sign(parameter_dict)
    parameter_dict['sign'] = str2hex(_gen_sign(sign_str, app_id))
    _LOGGER.info("_onepaycashpay query req data: %s, order_id is: %s", json.dumps(parameter_dict),
                 parameter_dict['order_no'])
    headers = {'Content-Type': 'application/x-www-form-urlencoded'}
    response = requests.post(_get_query_gateway(app_id), data=parameter_dict, headers=headers, timeout=3, verify=False)
    query_rsp_data = json.loads(response.text)['data']['row_detail'][0]
    _LOGGER.info("_onepaycashpay query rsp code: %s, data: %s, order_id: %s", response.status_code, response.text,
                 query_rsp_data['order_no'])
    if response.status_code == 200:
        mch_id = pay_order.mch_id
        trade_status = str(query_rsp_data['status'])
        total_fee = float(query_rsp_data['amount'])
        trade_no = query_rsp_data['payment_id']
        extend = {
            'trade_status': trade_status,
            'total_fee': total_fee,
            'trade_no': trade_no,
        }
        # 支付成功:PS_PAYMENT_SUCCESS 支付失败:PS_PAYMENT_FAIL
        if trade_status == 'PS_PAYMENT_SUCCESS':
            check_channel_order(pay_id, total_fee, app_id)
            _LOGGER.info('_onepaycashpay query order success, mch_id:%s pay_id:%s' % (mch_id, pay_order.id))
            order_db.add_pay_success(pay_order.mch_id, pay_order.id,
                                     total_fee, trade_no, extend)
            async_job.notify_mch(pay_order.id)
